﻿/**
 * @file MessageBaseService.hpp
 * @brief SSD仿真系统IPC消息基础服务类，负责消息分配、查找、队列操作等。
 */
#pragma once
#ifndef __MessageBaseService_h__
#define __MessageBaseService_h__

#include <memory>
#include <iostream>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/deque.hpp>

#include "Message.hpp"
#include "Constant.h"
#include "SynchronizedQueue.hpp"

using namespace boost::interprocess;

/**
 * @typedef MessageQueue
 * @brief 消息队列类型。
 */
typedef SynchronizedQueue<MessageId> MessageQueue;

/**
 * @class MessageBaseService
 * @brief IPC消息基础服务类模板，负责消息分配、查找、队列操作等。
 * @tparam TData 消息数据类型
 */
template <typename TData>
class MessageBaseService
{
protected:
    std::unique_ptr<managed_shared_memory> _ManagedShm; /**< 共享内存管理器 */
    U32 *_Counter;                                      /**< 消息ID计数器 */
    MessageQueue *_Queue;                               /**< 消息队列指针 */
    MessageQueue *_ResponseQueue;                       /**< 响应队列指针 */

public:
    /**
     * @brief 根据消息ID查找消息。
     * @param id 消息ID
     * @return 消息指针
     * @throws std::string 查找失败时抛出异常
     */
    Message<TData> *GetMessage(const MessageId &id)
    {
        std::string messageName = GetMessageName(id);
        auto message = _ManagedShm->find<Message<TData>>(messageName.c_str());
        if (message.first)
        {
            if (message.first->PayloadSize)
            {
                message.first->Payload = _ManagedShm->get_address_from_handle(message.first->_PayloadHandle);
            }
            return message.first;
        }

        std::stringstream ss;
        ss << "Cannot find message with id " << id;
        throw ss.str();
    }

protected:
    /**
     * @brief 分配新的消息对象。
     * @param payloadSize 载荷大小
     * @param expectsResponse 是否需要响应
     * @return 新分配的消息指针
     * @throws const char* 内存不足时抛出异常
     */
    Message<TData> *DoAllocateMessage(const U32 &payloadSize, const bool &expectsResponse)
    {
        void *payload = nullptr;
        boost::interprocess::managed_shared_memory::handle_t handle = 0;
        if (payloadSize)
        {
            payload = _ManagedShm->allocate(payloadSize, std::nothrow);
            if (payload == nullptr)
            {
                throw "Not enough memory to allocate message";
            }
            handle = _ManagedShm->get_handle_from_address(payload);
        }

        std::string messageName = GetMessageName(*_Counter);
        Message<TData> *message = _ManagedShm->construct<Message<TData>>(messageName.c_str())();
        message->_Id = *_Counter;
        message->PayloadSize = payloadSize;
        message->_PayloadHandle = handle;
        message->Payload = payload;
        message->_ExpectsResponse = expectsResponse;
        (*_Counter)++;

        return message;
    }

    /**
     * @brief 释放消息对象及其载荷。
     * @param message 要释放的消息指针
     */
    void DoDeallocateMessage(Message<TData> *message)
    {
        if (message->PayloadSize)
        {
            _ManagedShm->deallocate(message->Payload);
        }

        std::string messageName = GetMessageName(message->_Id);
        _ManagedShm->destroy<Message<TData>>(messageName.c_str());
    }

    /**
     * @brief 将消息ID推入队列。
     * @param queue 消息队列指针
     * @param message 消息指针
     */
    void DoPush(MessageQueue *&queue, Message<TData> *message)
    {
        queue->push(message->_Id);
    }

    /**
     * @brief 从队列弹出消息。
     * @param queue 消息队列指针
     * @return 弹出的消息指针，队列为空时返回nullptr
     */
    Message<TData> *DoPop(MessageQueue *&queue)
    {
        MessageId id;
        if (!queue->pop(id))
        {
            return nullptr;
        }

        return GetMessage(id);
    }

    /**
     * @brief 根据消息ID生成消息名称。
     * @param id 消息ID
     * @return 消息名称字符串
     */
    std::string GetMessageName(const MessageId &id)
    {
        std::ostringstream stringStream;
        stringStream << MESSAGE;
        stringStream << id;
        return stringStream.str();
    }
};

#endif